package top.ivansong92.example.leetcode.learning.data.struct.tree.bst;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class BST<E extends Comparable> {
    private int size;
    private Node root;

    public BST() {
        size = 0;
        root = null;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public void add(E e) {
        if (root == null) {
            root = new Node(e);
            size++;
            return;
        }
        addElement(root, e);
    }

    private void addElement(Node current, E e) {
        int comp = current.e.compareTo(e);
        if (comp == 0) {
            return;
        }

        if (comp < 0) {
            if (current.right == null) {
                current.right = new Node(e);
                size++;
            } else {
                addElement(current.right, e);
            }
        } else {
            if (current.left == null) {
                current.left = new Node(e);
                size++;
            } else {
                addElement(current.left, e);
            }
        }
    }

    private Node addElementV2(Node current, E e) {
        if (current == null) {
            size ++;
            return new Node(e);
        }
        int comp = current.e.compareTo(e);

        if (comp == 0) {
            return current;
        }

        if (comp > 0) {
            current.left = addElementV2(current.left, e);
        } else {
            current.right = addElementV2(current.right, e);
        }

        return current;
    }


    public E getMin() {
        if (root == null) {
            return null;
        }
        Node minNode = getMinNode(root);
        return minNode.e;
    }

    private Node getMinNode(Node node) {
        if (node.left == null) {
            return node;
        }
        return getMinNode(node.left);
    }

    public Node removeMin() {
        if (root == null) {
            return null;
        }
        Node minNode = getMinNode(root);
        root = removeMinNode(root);
        return minNode;
    }
    private Node removeMinNode(Node node) {
        if (node.left == null) {
            Node right = node.right;
            node.right = null;
            size --;
            return right;
        }
        node.left = removeMinNode(node.left);
        return node;
    }

    public Node removeMinNR() {
        if (root == null) {
            return null;
        }
        Node cur = root;
        Node left = root;
        while (left.left != null) {
            cur = left;
            left = left.left;
        }

        if (left != cur) {
            cur.left = left.right;
        } else {
            root = cur.right;
        }
        size --;
        return left;
    }

    public Node removeMaxNR() {
        if (root == null) {
            return null;
        }
        Node cur = root;
        Node right = root;
        while (right.right != null) {
            cur = right;
            right = right.right;
        }

        if (cur != right) {
            cur.right = right.right;
        } else {
            root = cur.left;
        }
        size --;
        return right;
    }


    public boolean contains(E e) {
        return contains(root, e);
    }


    public boolean containsV2(E e) {
        Node current = root;
        while (current != null) {
            int com = current.e.compareTo(e);
            if (com == 0) {
                return true;
            }
            if (com > 0) {
                current = current.left;
            } else {
                current = current.right;
            }
        }
        return false;
    }

    private boolean contains(Node node, E e) {
        if (node == null) {
            return false;
        }
        int com = node.e.compareTo(e);
        if (com == 0) {
            return true;
        }
        if (com > 0)  {
            return contains(node.left, e);
        } else {
            return contains(node.right, e);
        }
    }

    public void remove(E e) {

    }

    private Node removeNode(Node node, E e) {
        if (node == null) {
            return null;
        }
        int com = node.e.compareTo(e);
        if (com == 0) {
            if (node.left == null) {
                Node righ = node.right;
                node.right =  null;
                size --;
                return righ;
            }
            if (node.right == null) {
                Node left = node.left;
                node.left = null;
                size --;
                return left;
            }
            Node minProc = getMinNode(node);
            minProc.left = node.left;
            minProc.right = removeMinNode(node.right);
            node.right = node.left = null;
            return minProc;
        } else if (com > 0) {
            node.left = removeNode(node.left, e);
            return node;
        } else {
            node.right = removeNode(node.right, e);
            return node;
        }
    }
    private class Node {
        public E e;
        public Node left, right;

        public Node(E e) {
            this.e = e;
            left = null;
            right = null;
        }
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("size:" + size + ", nodes:\n");
        printNodesPre(root, 0, sb);
        return sb.toString();
    }

    public String toString1() {
        StringBuffer sb = new StringBuffer();
        sb.append("size:" + size + ", nodes:\n");
        printNodesMid(root, 0, sb);
        return sb.toString();
    }

    public void printNodesPreNR() {
        StringBuffer sb = new StringBuffer();
        sb.append("size:" + size + ", nodes:\n");
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        while (!stack.empty()) {
            Node cur = stack.pop();
            System.out.println(cur.e);
            if (cur.right != null) {
                stack.push(cur.right);
            }
            if (cur.left != null) {
                stack.push(cur.left);
            }
        }
    }

    //前序遍历
    private void printNodesPre(Node node, int depth, StringBuffer sb) {
        if (node == null) {
            sb.append(genDepthString(depth) + "null\n");
            return;
        }
        sb.append(genDepthString(depth) + node.e.toString() + "\n");
        printNodesPre(node.left, depth + 1, sb);
        printNodesPre(node.right, depth + 1, sb);
    }

    //中序遍历
    private void printNodesMid(Node node, int depth, StringBuffer sb) {
        if (node == null) {
            sb.append(genDepthString(depth) + "null\n");
            return;
        }
        printNodesMid(node.left, depth + 1, sb);
        sb.append(genDepthString(depth) + node.e.toString() + "\n");
        printNodesMid(node.right, depth + 1, sb);
    }

    //后序遍历
    private void printNodesAft(Node node, int depth, StringBuffer sb) {
        if (node == null) {
            sb.append(genDepthString(depth) + "null\n");
            return;
        }
        printNodesMid(node.left, depth + 1, sb);
        printNodesMid(node.right, depth + 1, sb);
        sb.append(genDepthString(depth) + node.e.toString() + "\n");
    }


    public void sequenceOrder() {
        Queue<Node> nodes = new LinkedList<>();
        nodes.add(root);
        while (!nodes.isEmpty()){
            Node cur = nodes.remove();
            System.out.println(cur.e);
            if (cur.left != null) {
                nodes.add(cur.left);
            }

            if (cur.right != null) {
                nodes.add(cur.right);
            }
        }
    }
    private String genDepthString(int depth) {
        StringBuilder stringBuilder = new StringBuilder();
        for(int i = 0; i < depth; i ++) {
            stringBuilder.append("-");
        }
        return stringBuilder.toString();
    }


    public static void main(String[] args) {
        BST<Integer> bst = new BST<>();
        bst.add(12);
        bst.add(98);
        bst.add(8);
        bst.add(1);
        bst.add(33);
        bst.add(9);
        System.out.println(bst.toString());
        System.out.println(bst.removeMin());
        System.out.println(bst.toString());
        bst.add(1);
        System.out.println(bst.removeMinNR());
        System.out.println(bst.toString());


        //bst.sequenceOrder();
    }
}
